#define TIME_PROFILE 0

#include <iostream>
#include <fstream>
#include <iomanip>
#include <csignal>

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <string.h>
#ifdef  DKL
#include <zlib.h>
#endif

# include <TFile.h>
# include <TH1.h>
# include <TLine.h>
# include <TF1.h>
# include <TROOT.h>
# include <TCanvas.h>
# include <TStyle.h>
# include <TApplication.h>
# include <TGaxis.h>
# include <TTree.h>
# include <TStopwatch.h>

#include "dkl_settings.h"
#include "teebuf.h"
#include "ConfigReader.h"
#if TIME_PROFILE
#include "timemeasure.h"
#endif

#include "DklHeader.h"
#ifdef DKL
#include "EventStruct.h"
#endif
#include "dackel_date.h"
#include "inconsistent.h"

#include "BufferedVMEReader.h"
#include "IOError.h"
#include "ADC_DL642A.h"
#include "TDC_V767.h"
#include "TimeCounter_DL643A1.h"
#include "Latch_SIS3600.h"
#include "DigitalIO_DL636F.h"
#include "Counter_DL636G1.h"
#include "StartStop.h"
#include "LogicPool/LogicQDCBoard.h"
#include "Disc_V812.h"
#include "disc_lib.h"


using namespace std;


// -------- event buffers ------- //
#define ADC_BUFSIZE   16384          //one word  per event
#define QDC_BUFSIZE   ADC_BUFSIZE
#define TC_BUFSIZE    ADC_BUFSIZE*2  //two words per event
#define LATCH_BUFSIZE ADC_BUFSIZE    //one word  per event
#define TDC_BUFSIZE   ADC_BUFSIZE    //one word  per event

// #define ADC_ADJUSTMENT
//defines, whether adjustment-subroutine will run
//when using ADC_ADJUSTMENT, min_delta and max_delta have to be at least 2 times the value of averagenum
//example for a dackel-cue: ./dackel -t 300 -r 10 -S 1 -H -20 -s 0 -n 2000 -m 3000

#ifdef ADC_ADJUSTMENT
int32_t        lowpeak[DET_NUMBER*PMT_PER_DET], highpeak[DET_NUMBER*PMT_PER_DET], averagenum=1000;
#endif

int16_t        qdcbuf[4][3][QDC_BUFSIZE];
u_int32_t      adcbuf[4][3][ADC_BUFSIZE];
u_int32_t      tcbuf[TC_BUFSIZE];
u_int32_t      tcbuf_chopper[TC_BUFSIZE];
u_int32_t      tcbuf_chopper_ring[TC_BUFSIZE];
u_int32_t      latchbuf[LATCH_BUFSIZE];
u_int32_t      tdcbuf[TDC_BUFSIZE][TDC_STOPS];
// counts the number of stops per event
u_int32_t      tdc_numstops[TDC_BUFSIZE]; 
u_int32_t      SelectorSpeed;
u_int32_t      ChopperSpeed;
u_int32_t      chopper_frequency = 94; // value in Hz

//u_int32_t  counterbuf[noc]; // size = noc * sfs_length * sf_loops
// will be allocated somewhere else
// see DklHeader.h


// -------- global variables -------- //
char *log_fn             = (char *) "dackel.log";
char *def_cfg_fn         = (char *) "dackel.cfg";
int adc0_addr            = 0xb100;
int adc1_addr            = 0xb200;
int adc2_addr            = 0xb300;
int adc3_addr            = 0xb400;
int qdc0_addr            = 0xf80000;
int qdc1_addr            = 0xf00000;
int qdc2_addr            = 0xe80000;
int qdc3_addr            = 0xe00000;
int tdc_addr             = 0;
int tdc_channel1         = DET1_CHANNEL;
int tdc_channel2         = DET2_CHANNEL;
int tdc_width            = 14;  //actual default value in dackel.cfg
int tdc_offset           = -4;  //actual default value in dackel.cfg
int tc_addr              = 0;
int tc_addr_chopper      = 0;
int latch_addr           = 0;
int cnt1_addr            = 0;
int cnt2_addr            = 0;
int io_addr              = 0;
int startstop_addr       = 0;
int discr_addr           = 0xee0000;
int lbox_conn            = 0;  //LogicBox not connected
int qdc_addr             = 0;
int qdc_start_delay      = 21;
int qdc_stop_delay	     = 17;
int qdc_gate_time        = 30;
int measuretime          = 10; //in seconds
int sleeptime            = 1; //microseconds
char *dev_fn             = (char *) "/dev/sis1100";
#ifdef DKL
char *out_fn             = (char *) "out.dkl";
#endif
char *root_fn            = (char *) "out.root";
int gz_level             = 3;
#ifdef DKL
int gz_strategy          = Z_DEFAULT_STRATEGY;
#endif
char *comment            = (char *) "alles fuer den dackel, alles fuer den club, "
"unser leben fuer den hund!";
u_int32_t min_delta      = 300;
u_int32_t max_delta      = 500;
u_int32_t tcbuf_chopper_ringsize = 1000;
u_int32_t tcbuf_chopper_ring_end = 0;
u_int32_t tdc_br_cs      = 1000;
u_int32_t tdc_br_cn      = 5;
char *sf_schema          = (char *) "01";
u_int32_t sfs_length     = 0;
u_int32_t sf_loops       = 2;
u_int32_t sf_pause       = 50000; //microseconds
bool only_test      	 = false;

// options flags for long options
int long_opt_flag        = 0;


// IO variables
streambuf *original_cerr_sb = 0;
TFile *out_root = NULL;
#ifdef DKL
int    out;
gzFile gzout;
double gzWriteTime = 0;
#endif
DklHeader * dklheader;


// hardware access
int                 crate=-1;
ADC_DL642A          *adc0,*adc1,*adc2,*adc3;
LogicQDCBoard		*qdc0,*qdc1,*qdc2,*qdc3;
TDC_V767            *tdc;
Latch_SIS3600       *latch;
TimeCounter_DL643A1 *tc,*tc_chopper;
Counter_DL636G1     *cnt1,*cnt2;
BufferedVMEReader   *tdc_br;
DigitalIO_DL636F    *io;
StartStop           *startstop;
CaenDiscriminator   *discr;

// Event data
TTree *		tree;
u_int32_t	Cycle;
Short_t     Pmt[DET_NUMBER][PMT_PER_DET];
UInt_t      DetSum[DET_NUMBER];
UInt_t      Det1Tdc[TDC_DET_STOPS];
UInt_t      Det2Tdc[TDC_DET_STOPS];
UInt_t      PmtTdc[DET_NUMBER][PMT_PER_DET][TDC_PMT_STOPS];
UInt_t		Latch;
u_int32_t	TimerCounter;
u_int32_t	DeltaTimerCounter = 0;
u_int32_t	TimerCounterChopper;
u_int32_t	DeltaTimerCounterChopper = 0;
Bool_t      SpinFlipState;
Bool_t      TdcNeedReset = false;
u_int32_t   EventNumber = 0;
UInt_t		DetFirst;

// Cycle Data
u_int32_t fcID           = 0;  // first cycle ID in file
u_int32_t cur_cycle      = 0;  // current offset from fcID (real cycle no = fcID + cur_cycle)
const u_int32_t noc      = 11; // number of counters 
							   // increased from 10 to 11 on 20070402 by alex
							   // the new counter indicates for every cycle wheter it is valid or not
							   // dackel from now continues, if the number of events in the several
							   // vme devices differ. the cycle then is invalid, a can not be used!
u_int32_t tdc_zeroevents;      // contains the number of events with zero tdc stops per cycle
u_int32_t consistency    = 0;  //should stay 0 if no error occurs;
u_int8_t  status_flags   = 0;
							/* status flags:
								bit 7: alternates between cycles (c0->0, c1->1, c2->0, c3->1,...)
								bit 6: spin flip state for cycle
                            	bit 5-3: number of tdc stops
    							bit 2-0: not used yet

								this is mapped to the highest 8 bit (i.e. bits 31-24) of the latch data
							*/
TTree *		CycleTree;
Bool_t 		Valid;
UInt_t		Counter[noc];

// Run Data
u_int32_t hitcounter=0, hitcounter_old=0, readcounter=0, readcounter_qdc=0, delta=0, qdc_delta=0;
u_int32_t delta_chopper=0, readcounter_chopper=0, chopper_buf_scan=0, last_tc_data_chopper=0, delta_chopper_ring=0;
u_int32_t expander_tc_data=0, expander_tc_data_chopper=0;
u_int32_t tcbuf_previous_read=0,tcbuf_read=0;
u_int32_t lastread=0;
u_int32_t ev_count=0; //contains the total number of events 
u_int32_t rate_hc=0, rate_tc=0;
double    rate;
u_int32_t display_hc=0;

int32_t   last_tdc_en=-1; // last tdc event num
u_int32_t tdc_eco=0;      // tdc event counter overflows

// this register contains information about the measurement modus
// bit  0: Beam
// bit  1: Main Shutter (H113)
// bit  2: Shutter-Up
// bit  3: Shutter-Down
// bit  4: Source 1
// bit  5: Source 2
// bit  6: Source 3
// bit  7: Source 4
// bit  8: Source 5
// bit  9: Scan
// bit 10: Test
u_int16_t modus=1;   // default is  measurement with beam




// -- functions in main.cpp -- //
void show_settings();
void show_help(char *progname);
void proc_opts(int argc,char *argv[]);
void read_config_file(int argc,char *argv[]);

void read_cycleno();
void write_nextcycleno();

void init_hw();
void reset_hw();
void read_hw();
u_int32_t readTDCEvent(u_int32_t *buf, u_int32_t bufsize,
					u_int32_t *tdc_numstops, u_int32_t *tdc_zeroevents) throw (IOError);
void setFlipper(unsigned char c);

void showErrorInfo( IOError &ioe );
void counterInfo();
void deadTime();

void write_event_data();
void write_run_data();
void write_cycle_data();
int GetTdcEvents(u_int32_t *tdc, u_int32_t bufsize, unsigned channel, UInt_t *dest, unsigned max_stops);
void rootfile_header_pre();
void rootfile_header_post();

void signal_handler(int signal);
void safe_exit(int r);




/*****************************************************************************/
int main(int argc, char *argv[])
/*****************************************************************************/
{
	signal(SIGINT,signal_handler); // install handler for CTRL-C

	original_cerr_sb = cerr.rdbuf(); // save original readbuffer of cerr

	stringstream tmplog;
	cerr.rdbuf( new teebuf(original_cerr_sb, tmplog.rdbuf()) );

	cerr << "----------------------------------------"
			<< "----------------------------------------\n"
			<< "dackel 2.0                            "
			<< "version: " << SVN_REV << ", " 
			<< "built: " << __DATE__ << " " << __TIME__ << endl
			<< "----------------------------------------"
			<< "----------------------------------------\n\n";

	read_config_file(argc,argv);  // process options from config_file
	proc_opts(argc,argv);         // process options given on command line

	if (only_test) { 
    	show_settings(); 
    	exit(0); 
	}

	// open log file
	cerr.flush();
	ofstream logs(log_fn);
	if(!logs) {
		cerr << "error: could not open log file \"" << log_fn << "\"" << endl;
		exit(1);
	}
	// from now on write directly to log file
	cerr.rdbuf(new teebuf(original_cerr_sb,logs.rdbuf()));

	// copy contents of tmplog to logfile!
	logs << tmplog.str();

	//determine the first cycles' number
	try { read_cycleno(); } catch( IOError ioe) {
		cerr << "---- ERROR: could not read cycle no! ----\n";
		cerr << ioe.getMsg();
		exit(1);
	}

	// open output file 	DklHeader * dklheader;

	dklheader = new DklHeader(modus, comment, sfs_length, sf_loops, fcID, noc);
	cerr << "dklheader:"
			<< " modus:0x" << hex << dklheader->modus << dec
			<< " comment:\"" << dklheader->comment
			<< "\" sfs_length:" << dec << sfs_length
			<< " sf_loops:" << dec << sf_loops
			<< " fcID: " << dec << fcID
			<< " noc:" << noc << endl << endl;

	cerr << "started "
			<< dklheader->date.year << "-"			
			<< (int)dklheader->date.month << "-"
			<< (int)dklheader->date.day << " " 
			<< (int)dklheader->date.hour << ":"
			<< (int)dklheader->date.minute << ":"
			<< (int)dklheader->date.second
			<< endl << endl;

#if TIME_PROFILE
	cerr << "compiled with TIME_PROFILE\ninitializing...";
	if(TM_INIT() !=0) { cerr << "error\n"; return 1; }
	cerr << "cpu frequency: "<< tm_cpu_freq << " Hz\n\n";
#endif

#ifdef DKL
	cerr << "open output file...";
	// open file for data output
	if( (out = creat(out_fn,0644))==-1 ) {
		cerr << "ERROR: could not open output file \"" << out_fn << "\"\n\n";
		cerr.flush(); logs.flush(); exit(1);
	}
	cerr << "ok, ";

	// write uncompressed header to outputfile
	cerr << "writing dklheader..."; dklheader->toFile(out);	cerr << "ok\n";

	// start compressed section of output file
	if( !(gzout = gzdopen(dup(out),"wb")) ) {
		cerr << "ERROR: gzdopen() returned zero\n\n";
		cerr.flush(); logs.flush(); exit(1);
	}
	if( gzsetparams(gzout,gz_level,gz_strategy) != Z_OK ) {
		cerr << "ERROR: gzsetparams() did not return Z_OK\n\n";
		cerr.flush(); logs.flush(); exit(1);      
	}
#endif

	// init root file
	cerr << "open ROOT output file...";
	out_root = new TFile(root_fn, "recreate");
	out_root->SetCompressionLevel(gz_level);
	
	rootfile_header_pre();
	
	// create ROOT event branches
	tree = new TTree("Data", "Data");
	tree->Branch("Cycle", &Cycle, "Cycle/i");		// I, S, .. signed, i,s, .. unsigned
	tree->Branch("Det1", &(Pmt[0][0]), "Det1[6]/S");
	tree->Branch("Det2", &(Pmt[1][0]), "Det2[6]/S");
	tree->Branch("DetSum", &(DetSum[0]), "Det1Sum/i:Det2Sum/i");
	tree->Branch("Time", &TimerCounter, "Time/i");
	tree->Branch("DeltaTime", &DeltaTimerCounter, "DeltaTime/i");
	tree->Branch("ChopperTime", &TimerCounterChopper, "ChopperTime/i");
	tree->Branch("DeltaChopperTime", &DeltaTimerCounterChopper, "DeltaChopperTime/i");
	tree->Branch("SpinFlipState", &SpinFlipState, "SpinFlipState/O");
	// TDC events per detector 
	char format[80];
	sprintf(format, "Det1Tdc[%i]/i", TDC_DET_STOPS);
	tree->Branch("Det1Tdc", &(Det1Tdc[0]), format);
	sprintf(format, "Det2Tdc[%i]/i", TDC_DET_STOPS);
	tree->Branch("Det2Tdc", &(Det2Tdc[0]), format);
	// TDC events per PMT
	sprintf(format, "Pmt1Tdc[%i][%i]/i:Pmt2Tdc[%i][%i]/i", PMT_PER_DET, TDC_PMT_STOPS, PMT_PER_DET, TDC_PMT_STOPS);
	tree->Branch("PmtTdc", &(PmtTdc[0][0]), format);
	tree->Branch("Latch", &Latch, "Latch/i");
	tree->Branch("DetFirst", &DetFirst, "DetFirst/i");		// 0: no DetTdc triggered, 1/2: Det1/2 triggered first
	
	// for debugging the TC problem
	tree->Branch("EventNumber", &EventNumber, "EventNumber/i");

	
	// create ROOT cycle branches
	CycleTree = new TTree("Cycle", "Cycle");
	CycleTree->Branch("Cycle", &Cycle, "Cycle/i");
	CycleTree->Branch("Valid", &Valid, "Valid/O");
	CycleTree->Branch("SelectorSpeed", &SelectorSpeed, "SelectorSpeed/i");
	CycleTree->Branch("ChopperSpeed", &ChopperSpeed, "ChopperSpeed/i");
	ostringstream formatstr;
	formatstr << "Counter[" << noc << "]/i" << flush;
	CycleTree->Branch("Counter", &Counter, formatstr.str().c_str());
	CycleTree->Branch("SpinFlipState", &SpinFlipState, "SpinFlipState/O");
	CycleTree->Branch("Status", &consistency, "Status/i");
	
	cerr << "ok, ";  // finished with ROOT setup
		

	// initialize hardware
	init_hw();
	logs.flush();

		
//  cerr << "measuring...\n";
	usleep(500);

	try {
		cur_cycle=0;
		
		for(u_int32_t sf_loop=0; sf_loop<sf_loops; sf_loop++) {

			cerr << "\nsl_loop: " << sf_loop << "\n";

			for(u_int32_t sfs_index=0; sfs_index < sfs_length; sfs_index++) {

				status_flags ^= 0x80; //toggle bit 7
				setFlipper(sf_schema[sfs_index]);

				cerr << "----------------------------------------"
						<< "----------------------------------------\n";

				cerr << "setting spin flipper to " << sf_schema[sfs_index]
						<< "  (status_flags=0x"
						<< hex << (u_int16_t)status_flags << dec << ") "
						<< "  cur. cycle: " << cur_cycle
						<< "  total: " << dklheader->cycle_count
						<< endl;

				usleep(sf_pause);

				time_t start = time(0);
				char timestr[100];
				strftime(timestr,100,"%y-%m-%d %H:%M:%S",localtime(&start));
				
// 				io->setO1(1);
				reset_hw();

				readcounter               = 0;
				readcounter_chopper       = 0;
				expander_tc_data          = 0;
				expander_tc_data_chopper  = 0;
				TimerCounter              = 0;
				DeltaTimerCounter         = 0;
				TimerCounterChopper       = 0;
				DeltaTimerCounterChopper  = 0;
				last_tc_data_chopper      = 0;	
				chopper_buf_scan          = 0;
				tcbuf_chopper_ring_end    = 0;
				memset(tcbuf_chopper_ring, 0, TC_BUFSIZE * sizeof(uint32_t));
				hitcounter                = 0;
				hitcounter_old            = 0;
				last_tdc_en = -1; tdc_eco = 0;
				consistency               = 0;
				tcbuf_previous_read       = 0;
				lastread                  = 0;
                display_hc                = 0;
				DetFirst				  = 0;
				
				usleep(100000);

				//CAUTION: measuretime must be given in seconds
				//         assuming a 10 MHz Clock is used
				startstop->reset();
				startstop->setPreset1((u_int32_t)(measuretime*STARTSTOP_CLOCK));
				startstop->setControlRegister(StartStop::COUNTER1_ENABLED);
				startstop->start();
				
				tdc_zeroevents=0;
				
				// time in seconds of the last display update
				int prev_clock_s = clock() / CLOCKS_PER_SEC;

				while(1) {
				
					// we're done once the StartStop card is finished
					if (startstop->getControlRegister() & StartStop::COUNTING_FINISHED)	
						break;
						
					// release some timeslices in every loop
					if (sleeptime != 0) 
						usleep(sleeptime);
						
					// take adc0/qdc0.1 as reference (arbitrary choice)
					if (lbox_conn) {
						delta = (*qdc0)[1].GetNEvents();
						hitcounter = hitcounter_old + delta;
					}
					else {
						hitcounter = adc0->getHitCounter();
						delta = hitcounter - readcounter;
					}

					//cerr << "delta " << delta << " hc " << hitcounter << endl;

					// limit number of events read
					if (max_delta > min_delta)
						if (delta > max_delta) 
							delta = max_delta;

					// update display once per second
					int curr_clock_s = clock() / CLOCKS_PER_SEC;
//                     if (curr_clock_s > prev_clock_s) {
                    if (delta >= min_delta) {
// 					if (1) {											
						prev_clock_s = curr_clock_s;
// 						cout << right << "\033[1Adt=" << setw(9)
#ifndef ADC_ADJUSTMENT
						cout << right << "\rdt=" << setw(3)
// 							<< startstop->getTimerCounter()
							<< startstop->getCounter1() / 10000000
							<< " de=" << setw(5) << hitcounter - display_hc  
							<< " delta=" << setw(3) <<delta
							<< "(" << setw(3) <<(hitcounter-readcounter) << ")"  
							<< setfill('0')
#ifdef DKL
							<< " lgzw_dt=" << setprecision(4)
							<< setw(9) << tm_secs
#endif
							<< "s rate=" << setprecision(4) << setw(5)
							<< rate << "kHz"
							<< "    " << flush;
#endif
						display_hc=hitcounter;
							
						logs.flush();
					}
						
					if( delta >= min_delta ) {
						delta -= (delta % 2 + 2);
// 						delta = delta - 10 - delta % 2;
						if (lbox_conn) hitcounter = hitcounter_old += delta;
						try {
							read_hw();
						} catch( IOError ioe ) {
							showErrorInfo( ioe );
							consistency |= INLOOP_ERROR;
							cerr << "\n>> THIS CYCLE WILL BE "
								<< "MARKED AS INVALID <<\n";
							cerr << "consistency=" << hex
								<< consistency << endl;

							dklheader->setCounter( CYCLE_VALID_COUNTER,
								cur_cycle, consistency );
							break;
						}

						try {
							write_event_data();
						} catch( IOError ioe ) {
							showErrorInfo( ioe );
							consistency |= WR_ERROR;
							cerr << "\n>> THIS CYCLE WILL BE "
								<< "MARKED AS INVALID <<\n";
							cerr << "consistency=" << hex
								<< consistency << endl;
							dklheader->setCounter( CYCLE_VALID_COUNTER,
								cur_cycle,consistency );
							break;
						}
							
						readcounter += delta;
						if (readcounter_qdc!=0) readcounter_qdc += delta;
						readcounter_qdc--;
						readcounter_chopper += delta_chopper;
					}
				}

				counterInfo();
				deadTime();
				
// 				io->setO1(0);

				cerr << "\nfinished sf_loop: ";

				uint32_t consistency_qdc = 0;
				if(!consistency) {
					// read the rest

					//take adc0 as reference
					if (lbox_conn) {
						hitcounter = hitcounter_old + (*qdc0)[1].GetNEvents();
						
						// read event counters of alls QDCs to verify consistency
						u_int32_t qdc0_hc, qdc1_hc, qdc2_hc;
						u_int32_t qdc3_hc, qdc4_hc, qdc5_hc;
						u_int32_t qdc6_hc, qdc7_hc, qdc8_hc;
						u_int32_t qdc9_hc, qdc10_hc, qdc11_hc;
						
						qdc0_hc = (*qdc0)[1].GetNEvents();
						qdc1_hc = (*qdc0)[2].GetNEvents();
						qdc2_hc = (*qdc0)[3].GetNEvents();
						qdc3_hc = (*qdc1)[1].GetNEvents();
						qdc4_hc = (*qdc1)[2].GetNEvents();
						qdc5_hc = (*qdc1)[3].GetNEvents();
						qdc6_hc = (*qdc2)[1].GetNEvents();
						qdc7_hc = (*qdc2)[2].GetNEvents();
						qdc8_hc = (*qdc2)[3].GetNEvents();
						qdc9_hc = (*qdc3)[1].GetNEvents();
						qdc10_hc = (*qdc3)[2].GetNEvents();
						qdc11_hc = (*qdc3)[3].GetNEvents();
						
						if (qdc0_hc != (hitcounter-readcounter) ||
							qdc1_hc != (hitcounter-readcounter) ||
							qdc2_hc != (hitcounter-readcounter) ) 
							consistency_qdc |= ADC0_INCONSISTENT;
						if (qdc3_hc != (hitcounter-readcounter) ||
							qdc4_hc != (hitcounter-readcounter) ||
							qdc5_hc != (hitcounter-readcounter) ) 
							consistency_qdc |= ADC1_INCONSISTENT;
						if (qdc6_hc != (hitcounter-readcounter) ||
							qdc7_hc != (hitcounter-readcounter) ||
							qdc8_hc != (hitcounter-readcounter) ) 
							consistency_qdc |= ADC2_INCONSISTENT;
						if (qdc9_hc != (hitcounter-readcounter) ||
							qdc10_hc != (hitcounter-readcounter) ||
							qdc11_hc != (hitcounter-readcounter) ) 
							consistency_qdc |= ADC3_INCONSISTENT;
					} else {
						hitcounter = adc0->getHitCounter();
					}
                    delta = hitcounter - readcounter;

					cerr << delta << " events left in buffers - reading..."
						<< flush;

					try {
						lastread = 1;
						read_hw();
					} catch( IOError ioe ) {
						showErrorInfo(ioe);
						consistency |= REMAIN_ERROR;
						cerr << "\n\n>> THIS CYCLE WILL BE "
							<< "MARKED AS INVALID <<\n";
						cerr << "consistency=" << hex
							<< consistency << endl;

						dklheader->setCounter(CYCLE_VALID_COUNTER,cur_cycle,consistency);
					}

					if( !consistency ) {
						cerr << "ok\n"; readcounter += delta;

						try {
							write_event_data();
						} catch( IOError ioe ) {
							showErrorInfo(ioe);
							consistency |= WR_ERROR;
							cerr << "\n\n>> THIS CYCLE WILL BE "
								<< "MARKED AS INVALID <<\n";
							cerr << "consistency=" << hex
								<< consistency << endl;
							dklheader->setCounter( CYCLE_VALID_COUNTER,cur_cycle,consistency );
						}
					}
				}
// finally removed by alex on 20070402 due to explicit wish of bastian ;-)
// 				cerr << "there have been " << tdc_eco
// 							<< " tdc event counter overflows (not an error)\n";

				if (tdc_zeroevents != 0) {
					cerr << "warning: there have been " << dec << tdc_zeroevents
							<< ( (tdc_zeroevents==1) ? " event " : " events ") 
							<< "with no TDC stops in cycle " << cur_cycle+fcID << "!" << endl;
					if (tdc_zeroevents > 1000) {
						TdcNeedReset = true;
						consistency |= TDC_ZEROEVENTS;
					}
				}
				
				//change on 20070402: now dackel continues if consistency check
				//fails, but marks the cycle as bad in one counter in DklHeader

				// check consistency
				u_int32_t adc0_hc;
				u_int32_t adc1_hc;
				u_int32_t adc2_hc;
				u_int32_t adc3_hc;
				
				u_int32_t tdc_hc =  (last_tdc_en+1) + tdc_eco*1024;
				u_int32_t latch_status = latch->getStatus();
				u_int32_t tc_words = tc->getWords();
								
				if (lbox_conn) {
					consistency |= consistency_qdc;
				} else {
					adc0_hc = adc0->getHitCounter();
					adc1_hc = adc1->getHitCounter();
					adc2_hc = adc2->getHitCounter();
					adc3_hc = adc3->getHitCounter();
					if (adc0_hc != readcounter) consistency |= ADC0_INCONSISTENT;
					if (adc1_hc != readcounter) consistency |= ADC1_INCONSISTENT;
					if (adc2_hc != readcounter) consistency |= ADC2_INCONSISTENT;
					if (adc3_hc != readcounter) consistency |= ADC3_INCONSISTENT;
				}

				if (tdc_hc != readcounter) consistency |= TDC_INCONSISTENT;
				if (!(latch_status&0x100)) consistency |= LATCH_INCONSISTENT;
				if (tc_words != 0 )        consistency |= TC_INCONSISTENT;
					
				// tell user what's wrong (_if_ there's something wrong ;))
				if (consistency) {
					cerr << "consistency check:";
					cerr << " code: " << hex << consistency << endl;
					if (!lbox_conn) {
						cerr
							<< " adc0: " << dec << adc0_hc
							<< (adc0_hc==readcounter?" ok":" error")
							<< "  adc1: " << adc1_hc
							<< (adc1_hc==readcounter?" ok":" error")
							<< "  adc2: " << adc2_hc
							<< (adc2_hc==readcounter?" ok":" error")
							<< "  adc3: " << adc3_hc
							<< (adc3_hc==readcounter?" ok":" error")
							<< endl;
					} else {
						cerr
							<< " qdc0: " << ((consistency & ADC0_INCONSISTENT) ? "error" : "ok")
							<< "  qdc1: " << ((consistency & ADC1_INCONSISTENT) ? "error" : "ok")
							<< "  qdc2: " << ((consistency & ADC2_INCONSISTENT) ? "error" : "ok")
							<< "  qdc3: " << ((consistency & ADC3_INCONSISTENT) ? "error" : "ok")
							<< endl;
					}
					cerr
							<< "   tdc: " << tdc_hc
							<< (tdc_hc==readcounter?" ok":" error")
							<< "  latch: "
							<< ((latch_status & 0x100) ?
								"empty: ok" : " not empty: error")
							<< "  tc: " << tc_words
							<< ((tc_words==0)?" ok\n":" error\n")
							<< endl;
										
					//only print this messages if there have been no errors
					//before the comparision of ADCs,TDC,TC and latch
					//(otherwise the messages has been already been print)
					if (consistency < REMAIN_ERROR) {
						cerr << "\n\n>> THIS CYCLE WILL BE "
							<< "MARKED AS INVALID <<\n";
						cerr << "consistency=" << hex
							<< consistency << endl;
					}
					TdcNeedReset = true;
				}

				cerr << "\n             cycle:  total:  d1:  d2: "
					<< "(counts per cycle) time() timestr\n"
					<< "CYCLEINFO:00: "
					<< cur_cycle+fcID << " "
					<<	hitcounter << " "
					<< cnt1->getA() << " "
					<< cnt1->getB() << " "
					<< start << " "
					<< timestr << endl;

				write_cycle_data();

				// keep track of run data                
				ev_count += hitcounter;

				// increase cycle number
				cur_cycle++; 
				
				// save new cycle number to file
				write_nextcycleno();
			}
		}

	} catch(IOError ioe) {
		showErrorInfo(ioe);
		cerr.flush(); logs.flush(); 
		safe_exit(1);
	}

	// turning off spinflipper (2007-03-06)
	setFlipper('0');

	close(crate);
#ifdef DKL		
	gzclose(gzout); //this ends the compressed section in the output file
#endif


	dklheader->fileOK = 0; // ok, there were no errors so far
	dklheader->ev_count = ev_count;


	// schreibe Tree
	tree->Write();
		
	// schreibe Header Informationen
	rootfile_header_post();
		
	// schliesse output-Datei
	out_root = tree->GetCurrentFile();
	out_root->Write();
	out_root->Close();
	
	if (qdc0) {
		qdc0->Close();
		qdc1->Close();
		qdc2->Close();
		qdc3->Close();
	}

	cerr << ev_count << " events total" << endl;
#ifdef DKL
	cerr
		<< "meantime for gzwrite: "
		<< gzWriteTime/hitcounter << " seconds\n\n";
#endif

	
	cerr << dklheader->infoString() << flush;

#ifdef DKL
	//write file status: ok and number of events to header of output file
	lseek(out,0,0);
	try	{	dklheader->toFile(out); }
	catch(IOError ioe) {
		cerr << ioe.getMsg();
		exit(1);
	}
		
	close(out);
#endif

	// make sure we display all output
	cerr.flush(); 
	return 0;
}
/*****************************************************************************/


/*---------------------------------------------------------------------------*/
void deadTime()
/*---------------------------------------------------------------------------*/
{
	cerr << "\ndeadtime = ("
			<< ((startstop->getCounter2() - startstop->getCounter1())/10)
			<< "+-"
			<< ((cnt2->getB() - startstop->getCounter1())/10)
			<< ")us  <=>  "
			<<	(((double)startstop->getCounter2()
				-startstop->getCounter1())
				/startstop->getCounter1() * 100 ) << "%"
			<< endl;
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void counterInfo()
/*---------------------------------------------------------------------------*/
{
	//for unknown reason this has to be done
	//since the startstop card does only give the correct value on the second attempt
	startstop->getCounter1();

	cerr << "\nstartstop: cnt1=" << startstop->getCounter1()
			<< ",cnt2=" << startstop->getCounter2()
//			 << ",cnt3=" << startstop->getCounter3()
//			 << ",timer=" << startstop->getTimerCounter() 
			<< " counter2: A=" << cnt2->getA()
			<< ",B=" << cnt2->getB()
			<< ",C=" << cnt2->getC() << endl;
}
/*---------------------------------------------------------------------------*/


//TODO: give human readable info
/*---------------------------------------------------------------------------*/
void showErrorInfo( IOError &ioe )
/*---------------------------------------------------------------------------*/
{
	cerr << "catched IOError:\n" << ioe.getMsg() << endl
			<< "param: "
			<< ioe.getParam() << " (signed) = "
			<< (u_int32_t)ioe.getParam() << " (unsigned) = " << hex
			<< ioe.getParam() << " (hex)"
			<< endl << endl;

	cerr << "reading out module states:\n";
	try {
		cerr << hex
				<< "tdc: status1=0x" << tdc->getStatus1()
				<< " status2=0x" << tdc->getStatus2()
				<< " errorcode_tdc0=0x" << tdc->readTDCErrorCode(0)
				<< " errorcode_tdc1=0x" << tdc->readTDCErrorCode(1)
				<< "\n\n" << flush;
		if (lbox_conn) {
			//TODO: add status report here
		} else {
			cerr << hex
					<< "adc0: status=0x" << adc0->getStatus() << endl
					<< "adc1: status=0x" << adc1->getStatus() << endl
					<< "adc2: status=0x" << adc2->getStatus() << endl 
					<< "adc3: status=0x" << adc3->getStatus() << endl;
		}
		cerr << hex
				<< "latch: status=0x" << latch->getStatus() << endl
				<< "\n";
	} catch(IOError ioe) {
		cerr << "could not read status, catched IOError:\n" << ioe.getMsg()
				<< endl
				<< "param: "
				<< ioe.getParam() << " (signed) = "
				<< (u_int32_t)ioe.getParam() << " (unsigned) = " << hex
				<< ioe.getParam() << " (hex)"
				<< endl << endl;
	}

	cerr << "hitcounter = " << dec << hitcounter << endl
			<< "readcounter = " << dec << readcounter << endl;
}
/*---------------------------------------------------------------------------*/



/*---------------------------------------------------------------------------*/
void setFlipper(unsigned char c)
/*---------------------------------------------------------------------------*/
{
	if(c=='1') {
		io->setO2(1); // spin flipper = 1
		status_flags |= 0x40; // sets bit 6 to 1
		SpinFlipState = true;
	} else if (c=='0') {
		io->setO2(0); // spin fliper = 0
		status_flags &= ~0x40; // sets bit 6 to 0
		SpinFlipState = false;
	} 
	else 
		throw IOError("setFlipper(): invalid Flipper state!",c);
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void init_hw()
/*---------------------------------------------------------------------------*/
{
	// open sis1100/3100 pci-to-vme link
	if ( !(crate = open(dev_fn, O_RDWR, 0))) {
		cerr << "ERROR: could not open \"" << dev_fn << "\"" << endl;
		cerr.flush();
		exit(1);
	}

	// retrieve 
	int sisver = vmedriverversion(crate);
	if (sisver < 0)
		cerr << "ERROR: failed to retrieve driver version!" << endl;
	else
		cerr << "SIS driver revision: " << (sisver >> 16) << "." << (sisver & 0xffff) << endl;

	// instantiate vme objects
	if (lbox_conn) {
		qdc0 = new LogicQDCBoard(crate, qdc0_addr);
		qdc1 = new LogicQDCBoard(crate, qdc1_addr);
		qdc2 = new LogicQDCBoard(crate, qdc2_addr);
		qdc3 = new LogicQDCBoard(crate, qdc3_addr);
		// terminate only last trigger input
		//qdc3->GetDIO(4).SetTermination();
		// qdc1 = qdc2 = qdc3 = qdc0;
		adc0 = adc1 = adc2 = adc3 = NULL;
	} else {
		adc0   = new ADC_DL642A(crate, adc0_addr);
		adc1   = new ADC_DL642A(crate, adc1_addr);
		adc2   = new ADC_DL642A(crate, adc2_addr);
		adc3   = new ADC_DL642A(crate, adc3_addr);
		qdc0 = qdc1 = qdc2 = qdc3 = NULL;
	}

	tdc    = new TDC_V767(crate, tdc_addr);
	tdc_br = new BufferedVMEReader(tdc_br_cs, tdc_br_cn, tdc);
	latch  = new Latch_SIS3600(crate, latch_addr);
	tc     = new TimeCounter_DL643A1(crate, tc_addr);
	tc_chopper = new TimeCounter_DL643A1(crate, tc_addr_chopper);
	cnt1   = new Counter_DL636G1(crate, cnt1_addr);
	cnt2   = new Counter_DL636G1(crate, cnt2_addr);
	io     = new DigitalIO_DL636F(crate, io_addr);
	startstop = new StartStop(crate, startstop_addr);
	discr  = new Disc_V812(crate, discr_addr);
	
	cerr << "hw init: vmesysreset..." << flush;
	if(int r = vmesysreset(crate)) {
		cerr << "error: vmesysreset() returned " << r << endl << endl;
		cerr.flush();
		safe_exit(1);
	}
	cerr << "ok, sleeping 1 sec..."; sleep(1); cerr << "ok, ";

//	show_SIS3100_status(crate);

	try {
		//reset of startstop has to be done first!!!
		cerr << " init:\n" << flush;
		startstop->reset();
		cerr << "ok, ";

//		startstop->setTimerPreset(measuretime);
//		startstop->setControlRegister(StartStop::TIMERPRESET_ENABLED | mtu );

		// init io
		cerr << "io..." << flush;
		io->setOutputs(0); //set all output to zero
		cerr << "ok, ";

		if (lbox_conn) {
			// init new LogicPool QDC
			cerr << "QDCs...";
			cerr << "(LogicPool ID: " 
				<< hex << qdc0->GetInterface().GetID() << ", "
				<< hex << qdc1->GetInterface().GetID() << ", "
				<< hex << qdc2->GetInterface().GetID() << ", "
				<< hex << qdc3->GetInterface().GetID() << ")" << flush;

			// set QDC parameters
			qdc0->SetStartDelay(qdc_start_delay);
			qdc0->SetStopDelay(qdc_stop_delay);
			qdc0->SetGateTime(qdc_gate_time);
			qdc1->SetStartDelay(qdc_start_delay);
			qdc1->SetStopDelay(qdc_stop_delay);
			qdc1->SetGateTime(qdc_gate_time);
			qdc2->SetStartDelay(qdc_start_delay);
			qdc2->SetStopDelay(qdc_stop_delay);
			qdc2->SetGateTime(qdc_gate_time);
			qdc3->SetStartDelay(qdc_start_delay);
			qdc3->SetStopDelay(qdc_stop_delay);
			qdc3->SetGateTime(qdc_gate_time);
		} else {
			// init ADCs
			cerr << "ADCs..." << flush;
			adc0->reset(); adc0->clearFIFO(); adc0->setEnabled(1);
			adc1->reset(); adc1->clearFIFO(); adc1->setEnabled(1);
			adc2->reset(); adc2->clearFIFO(); adc2->setEnabled(1);
			adc3->reset(); adc3->clearFIFO(); adc3->setEnabled(1);
		}
		cerr << "ok, ";

		// init discriminator
		cerr << "discriminator..." << flush;
		disc_read_ini(*discr, (char *)"disc.ini"); //FIXME: ini file is hardcoded
		// TODO: read ini files only once
		cerr << "ok, ";
		
		// init TDC
		// TODO: set edge detection on all channels!
		cerr << "tdc..." << flush;
		tdc->reset(); 
		sleep(2);
		tdc->setMode(TDC_V767::STOP_MATCH);
		tdc->setWindowWidth(tdc_width);
		tdc->setWindowOffset(tdc_offset);
		tdc->disableAllChannels();

		tdc->enableChannel(tdc_channel1);
		tdc->enableChannel(tdc_channel2);

		// WARNING: hard coded mapping here
		//  detector 1
		for (int ch = 8; ch < 14; ch ++)
			tdc->enableChannel(ch);
		// detector 2
		//for (int ch = 18; ch < 24; ch ++)
		//	tdc->enableChannel(ch);

		tdc->enSubTriggerTime();
		tdc->setRisingEdgeAll();
		tdc->setDataReady();
		cerr << "ok, ";

		// init Latch
		cerr << "latch..." << flush;
		latch->reset();
		//removed 20060406_2346
		//latch->clrCoincidenceMode();
		//added 20060406_2346             //TAG: BASTIAN
		latch->setCoincidenceMode();
		latch->enNextClkLogic();
		latch->enExtNextMode(); //enable external trigger
		cerr << "ok, ";

		// init TimeCounter
		cerr << "tc..." << flush; 
		tc->reset(); 
		cerr << "ok, ";

		cerr << "tc-chopper..." << flush; 
		tc_chopper->reset(); 
		cerr << "ok, ";

		// init Counter
		cerr << "counters..." << flush;
		cnt1->reset(); 
		cnt2->reset();
		cerr << "ok, ";
		
		if (lbox_conn) {
			cerr << "\n\nmeasurement with NEW QDC" << endl;
		} else {
			cerr << "\n\nmeasurement with OLD ADC" << endl;
		}
		
		// init StartStop
//		cerr << setw(24) << "init startstop module";
//		startstop->reset();
//		cerr << "ok\n";
	} catch(IOError ioe) {
		cerr << "catched IOError:\n" << ioe.getMsg() << endl
			<< "param: "
			<< ioe.getParam() << " (signed) = "
			<< (u_int32_t)ioe.getParam() << " (unsigned) = " << hex
			<< ioe.getParam() << " (hex)\n\n"
			<< "strerror() returns: " << strerror(errno)
			<< endl << endl;

		cerr.flush();
		safe_exit(1);
	}
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void reset_hw()
/*---------------------------------------------------------------------------*/
{
	if (TdcNeedReset) {
		// additional reset in case of tdc_zeroevents > 0
		cerr << "\n\n!! additional reset for tdc..." << flush;
		tdc->reset(); 
		sleep(2);
		tdc->setMode(TDC_V767::STOP_MATCH);
		tdc->setWindowWidth(tdc_width);
		tdc->setWindowOffset(tdc_offset);
		tdc->disableAllChannels();

		tdc->enableChannel(tdc_channel1);
		tdc->enableChannel(tdc_channel2);
/*
		//TODO: test this change before committing
		for (int ch = 0; ch < 16; ch ++)
		tdc->enableChannel(ch);
*/            
		tdc->enSubTriggerTime();
		tdc->setRisingEdgeAll();
		tdc->setDataReady();
		cerr << "ok !! \n\n" << endl;
		TdcNeedReset = false;
		tdc_br = new BufferedVMEReader(tdc_br_cs, tdc_br_cn, tdc);
	}
	
	tc->reset();
	tc_chopper->reset();
    tc->resetFIFOFF();
    tc_chopper->resetFIFOFF();
	
	cnt1->reset();
	cnt2->reset();
	latch->clearFIFO();

	if (lbox_conn) {
		qdc0->Clear(); 
		qdc1->Clear(); 
		qdc2->Clear(); 
		qdc3->Clear(); 
// 		io->setO1(1); //for testing of QDC-busy
	} else {
		adc0->reset(); adc0->clearFIFO(); adc0->setEnabled(1);
		adc1->reset(); adc1->clearFIFO(); adc1->setEnabled(1);
		adc2->reset(); adc2->clearFIFO(); adc2->setEnabled(1);
		adc3->reset(); adc3->clearFIFO(); adc3->setEnabled(1);
// 		io->setO1(1);
	}
	
	/// tdcs are cleared, output buffer is cleared,
	/// readout controller is reset, event counter is set to 0
	tdc->clearRegisters();   
	//tdc_br->clear();
									
    tc->reset();
    tc_chopper->reset();
    tc->resetFIFOFF();
    tc_chopper->resetFIFOFF();
}


/*---------------------------------------------------------------------------*/
void read_config_file(int argc,char *argv[])
/*---------------------------------------------------------------------------*/
{
	// get config file name
	ConfigReader cr;
	try {
		cr.getConfigFileName(&argc, argv);
	} catch(IOError ioe) {
		cerr << ioe.getMsg() << endl << endl;
		exit(1);
	}

	// look if default config file exists
	if ((cr.cfg_filename == 0) & (def_cfg_fn != 0)) {
		cerr << "no config file given, looking for default config file \""
				<< def_cfg_fn << "\"" << endl << endl;
		ifstream dummy(def_cfg_fn, ios::in);
		if (dummy.is_open()) {
			cr.cfg_filename = strdup(def_cfg_fn);
			dummy.close();
		} 
	}

	// read config file
	if (cr.cfg_filename) {
		cerr << "using config file \"" << cr.cfg_filename << "\"\n\n";
		try {	
			cr.readConfigFile(); 
		}
		catch(IOError ioe) { 
			cerr << ioe.getMsg() << "\n\n"; 
			exit(1); 
		}
	} else 
		cerr << "using no config file\n\n";
	proc_opts(cr.fargc, cr.fargv); // process options from config file
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void show_settings()
/*---------------------------------------------------------------------------*/
{
	cerr << "settings:" << endl;
	cerr << hex 
		<< setw(20) << right << "adc0_addr = " << "0x" << adc0_addr  << endl
		<< setw(20) << right << "adc1_addr = " << "0x" << adc1_addr  << endl
		<< setw(20) << right << "adc2_addr = " << "0x" << adc2_addr  << endl
		<< setw(20) << right << "adc3_addr = " << "0x" << adc3_addr  << endl
		<< setw(20) << right << "qdc0_addr = " << "0x" << qdc0_addr  << endl
		<< setw(20) << right << "qdc1_addr = " << "0x" << qdc1_addr  << endl
		<< setw(20) << right << "qdc2_addr = " << "0x" << qdc2_addr  << endl
		<< setw(20) << right << "qdc3_addr = " << "0x" << qdc3_addr  << endl
		<< setw(20) << right << "tdc_addr = "  << "0x" << tdc_addr   << endl
	     << dec
		<< setw(20) << right << "tdc_channel1 = " << tdc_channel1 << endl
		<< setw(20) << right << "tdc_channel2 = " << tdc_channel2 << endl
		<< setw(20) << right << "tdc_width = "    << tdc_width    << endl
		<< setw(20) << right << "tdc_offset = "   << tdc_offset   << endl
	     << hex
		<< setw(20) << right << "latch_addr = "     << "0x" << latch_addr << endl
		<< setw(20) << right << "io_addr = "        << "0x" << io_addr    << endl
		<< setw(20) << right << "cnt1_addr = "      << "0x" << cnt1_addr  << endl
		<< setw(20) << right << "cnt2_addr = "      << "0x" << cnt2_addr  << endl
		<< setw(20) << right << "tc_addr = "        << "0x" << tc_addr    << endl
		<< setw(20) << right << "tc_addr_chopper = "<< "0x" << tc_addr_chopper << endl
		<< setw(20) << right << "startstop_addr = " << "0x" << startstop_addr << endl
		<< setw(20) << right << "discr_addr = "     << "0x" << discr_addr << endl
	     << dec
		<< setw(20) << right << "measuretime = "  << measuretime << endl
		<< setw(20) << right << "chopper_frequency = " << chopper_frequency << endl
		<< setw(20) << right << "sleeptime = "    << sleeptime  << " us" << endl
		<< setw(20) << right << "dev_fn = "       << dev_fn     << endl
#ifdef DKL
		<< setw(20) << right << "out_fn = "       << out_fn     << endl
#endif
		<< setw(20) << right << "root_fn = "      << root_fn    << endl
		<< setw(20) << right << "log_fn = "       << log_fn     << endl
		<< setw(20) << right << "gz_level = "     << gz_level   << endl
		<< setw(20) << right << "sf_schema = "    << sf_schema  << endl
		<< setw(20) << right << "sfs_length = "   << sfs_length << endl
		<< setw(20) << right << "sf_loops = "     << sf_loops   << endl
		<< setw(20) << right << "sf_pause = "     << sf_pause   << " us" << endl
		<< setw(20) << right << "only_test = "    << only_test  << endl
		<< setw(20) << right << "max_delta = "    << max_delta  << endl
		<< setw(20) << right << "min_delta = "    << min_delta  << endl
		<< setw(20) << right << "qdc_start_delay = " << qdc_start_delay << endl
		<< setw(20) << right << "qdc_stop_delay = "  << qdc_stop_delay << endl
		<< setw(20) << right << "qdc_gate_time = "   << qdc_gate_time << endl
		<< setw(20) << right << "lbox_conn = "       << lbox_conn  << endl
		<< setw(20) << right << "comment = "      << comment    << endl
		<< setw(20) << right << "mode = "         << modus      << endl
		<< endl 
	     << dec;
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void show_help(char *progname)
/*---------------------------------------------------------------------------*/
{
	cerr
			<< "usage: " << progname << " [options]\n\n"
			<< "options:" << endl <<
			" -h, --help                               shows this help\n"
			" -e, --test                               only show settings and exit\n"
			" -f <file>,   --config-file <file>        use <file> as config file\n"
			" -c <comment>,--comment <comment>         comment for measurement\n"
			"\n"
			" ADC settings:\n"
			" -A <addr>,   --adc0-addr <addr>          set address of adc0 (in hex)\n"
			" -B <addr>,   --adc1-addr <addr>          set address of adc1 (in hex)\n"
			" -C <addr>,   --adc2-addr <addr>          set address of adc2 (in hex)\n"
			" -D <addr>,   --adc3-addr <addr>          set address of adc3 (in hex)\n\n"
			" LogicPool QDC settings:\n"
			"              --qdc0-addr <addr>          set address of qdc0 (in hex)\n"
			"              --qdc1-addr <addr>          set address of qdc1 (in hex)\n"
			"              --qdc2-addr <addr>          set address of qdc2 (in hex)\n"
			"              --qdc3-addr <addr>          set address of qdc3 (in hex)\n"
			"              --qdc-start-time <time>     set start time (in 10ns)\n"
			"              --qdc-stop-time <time>      set stop time (in 10ns)\n"
			"              --qdc-gate-time <time>      set gate time (in 10ns)\n"
			" TDC settings:\n"
			" -T <addr>,   --tdc-addr <addr>           set tdc address (in hex)\n"
			" -E <ch#>,    --tdc-channel1 <ch#>        set tdc channel for detector 1\n"
			" -F <ch#>,    --tdc-channel2 <ch#>        set tdc channel for detector 2\n"
			" -G <tics>,   --tdc-window-width <tics>   set tdc window width (clock tics)\n"
			" -H <tics>,   --tdc-window-offset <tics>  set tdc offset (clock tics)\n\n"
			" other settings:\n"
			" -U <addr>,   --tc-addr <addr>            set tc address (in hex format)\n"
			" -V <addr>,   --tc-addr-chopper <addr>    set tc-chopper address (in hex format)\n"
			" -L <addr>,   --latch-addr <addr>         set latch address (in hex format)\n"
			" -I <addr>,   --io-addr <addr>            set io module address (in hex)\n"
			" -J <addr>,   --cnt1-addr <addr>          set counter1 address (in hex)\n"
			" -K <addr>,   --cnt2-addr <addr>          set counter2 address (in hex)\n"
			" -M <addr>,   --startstop-addr <addr>     set startstop address (in hex)\n"
			" -d <path>,   --sis1100-device <path>     set sis1100 device file to <path>\n"
			" -t <time>,   --measure-time <time>       set measure time (in s)\n"
			" -R <freq>,   --chopper-frequency <Hz>    set chopper frequency (in Hz)\n"
			" -s <time>,   --sleep-time <time>         set sleep time (in us)\n"
			" -z <level>,  --gz-level <level>          set gz compression level\n"
			" -o <file>,   --data-file <file>          set output file\n"
			" -l <file>,   --log-file <file>           set log file\n"
			" -S <schema>, --spin-flip-schema <schema> set spin flip schema\n"
			" -r <loops>,  --spin-flip-loops <loops>   set number of spin flip loops\n"
			" -p <time>,   --spin-flip-pause <time>    pause between spinflips (in us)\n"
			" -m <events>, --max-delta <events>        max_number of events per readout\n"
			" -n <events>, --min-delta <events>        min_number of events per readout\n"
			"\nMeasurement mode flags:\n"
			" -q <n>,      --src <n>                   using calibration source n\n"
			" -a,          --scan                      a scan is done\n"
			" -N,          --h1c                       shutter h113 closed\n"
			" -O,          --sdc                       shutter down closed\n"
			" -P,          --suc                       shutter up closed\n"
			" if no mode flag is given, it is assumed that all shutters are open\n"
			" and no calibration sources are used.\n"
			"\n"
			"all numbers must be given in decimal format, if not explicitly specified\n"
			<< endl;
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void proc_opts(int largc, char *largv[])
/*---------------------------------------------------------------------------*/
{
	static struct option long_options[] =
			{
				{"adc0-addr",         required_argument, 0, 'A'},
				{"adc1-addr",         required_argument, 0, 'B'},
				{"adc2-addr",         required_argument, 0, 'C'},
				{"adc3-addr",         required_argument, 0, 'D'},
				{"tdc-channel1",      required_argument, 0, 'E'},
				{"tdc-channel2",      required_argument, 0, 'F'},
				{"tdc-window-width",  required_argument, 0, 'G'},
				{"tdc-window-offset", required_argument, 0, 'H'},
				{"io-addr",           required_argument, 0, 'I'},
				{"cnt1-addr",         required_argument, 0, 'J'},
				{"cnt2-addr",         required_argument, 0, 'K'},
				{"latch-addr",        required_argument, 0, 'L'},
				{"startstop-addr",    required_argument, 0, 'M'},
				{"h1c",               no_argument,       0, 'N'},
				{"sdc",               no_argument,       0, 'O'},
				{"suc",               no_argument,       0, 'P'},
				{"chopper-frequency", required_argument, 0, 'R'},
				{"qdc-addr",          required_argument, 0, 'Q'},
				{"spin-flip-schema",  required_argument, 0, 'S'},
				{"tdc-addr",          required_argument, 0, 'T'},
				{"tc-addr",           required_argument, 0, 'U'},
				{"tc-addr-chopper",   required_argument, 0, 'V'},
				{"discr-addr",        required_argument, 0, 'W'},
				{"scan",              no_argument,       0, 'a'},
				{"comment",           required_argument, 0, 'c'},
				{"sis1100-device",    required_argument, 0, 'd'},
				{"test",              no_argument,       0, 'e'},
				{"config-file",       required_argument, 0, 'f'},
				{"help",              no_argument,       0, 'h'},
				{"log-file",          required_argument, 0, 'l'},
				{"max-delta",         required_argument, 0, 'm'},
				{"min-delta",         required_argument, 0, 'n'},
				{"data-file",         required_argument, 0, 'o'},
				{"spin-flip-pause",   required_argument, 0, 'p'},
				{"src",               required_argument, 0, 'q'},
				{"spin-flip-loops",   required_argument, 0, 'r'},
				{"sleep-time",        required_argument, 0, 's'},
				{"measure-time",      required_argument, 0, 't'},
				{"lbox-conn",         required_argument, 0, 'v'},
				{"qdc0-addr",         required_argument, &long_opt_flag, 1},
				{"qdc1-addr",         required_argument, &long_opt_flag, 2},
				{"qdc2-addr",         required_argument, &long_opt_flag, 3},
				{"qdc3-addr",      	  required_argument, &long_opt_flag, 4},
				{"qdc-start-delay",   required_argument, &long_opt_flag, 5},
				{"qdc-stop-delay",    required_argument, &long_opt_flag, 6},
				{"qdc-gate-time",     required_argument, &long_opt_flag, 7},
				{"gz-level",          required_argument, 0, 'z'},
				{0, 0, 0, 0}
			};
			

	optind=0; // important! if getopt_long() is used to processes
	// more than one array, as done in main()
	while(1) {
		/* getopt_long stores the option index here. */
		int option_index = 0;
		int c;
		unsigned tmp;

		// important: without this statement getopt_long() does not
		//            terminate processing of arguments correctly
		if (optind>=largc) break;

		c = getopt_long(largc, largv,
						"A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:R:S:T:U:V:W:"
						"c:d:ef:hl:m:n:o:p:q:r:s:t:u:v:z:",
						long_options, &option_index);    

		if (c == -1) break; // Detect the end of the options.
		// this does not work correctly if you use
		// cr.fargc and cr.fargv! (see above)

		switch(c) {
			case 'h':
				show_help(largv[0]);
				exit(0);
				break;
			case 'e':
				only_test = true;
				break;
			case 'f':
				// option is handled by ConfigReader object, ignore
				break;
			case 'A':
				sscanf(optarg,"%x",&adc0_addr);
				break;
			case 'B':
				sscanf(optarg,"%x",&adc1_addr);
				break;
			case 'C':
				sscanf(optarg,"%x",&adc2_addr);
				break;
			case 'D':
				sscanf(optarg,"%x",&adc3_addr);
				break;
			case 'R':
				sscanf(optarg,"%u",&chopper_frequency);
				break;
			case 'Q':
				sscanf(optarg,"%x",&qdc_addr);
				break;
			case 'T':
				sscanf(optarg,"%x",&tdc_addr);
				break;
			case 'E':
				sscanf(optarg,"%d",&tdc_channel1);
				break;
			case 'F':
				sscanf(optarg,"%d",&tdc_channel2);
				break;
			case 'G':
				sscanf(optarg,"%d",&tdc_width);
				break;
			case 'H':
				sscanf(optarg,"%d",&tdc_offset);
				break;
			case 'U':
				sscanf(optarg,"%x",&tc_addr);
				break;
			case 'V':
				sscanf(optarg,"%x",&tc_addr_chopper);
				break;
			case 'W':
				sscanf(optarg,"%x", &discr_addr);
				break;
			case 'L':
				sscanf(optarg,"%x",&latch_addr);
				break;
			case 'I':
				sscanf(optarg,"%x",&io_addr);
				break;
			case 'J':
				sscanf(optarg,"%x",&cnt1_addr);
				break;
			case 'K':
				sscanf(optarg,"%x",&cnt2_addr);
				break;						
			case 'v':
				sscanf(optarg,"%x",&lbox_conn);
				break;
			case 'M':
				sscanf(optarg,"%x",&startstop_addr);
				break;
			case 't':
				sscanf(optarg,"%d",&measuretime);
				break;
			case 's':
				sscanf(optarg,"%d",&sleeptime);
				break;
			case 'z':
				sscanf(optarg,"%d",&gz_level);
				break;
			case 'd':
				dev_fn = strdup(optarg);
				break;
#ifdef DKL
			case 'o':
				out_fn = strdup(optarg);
				break;
#else
			case 'o':
				root_fn = strdup(optarg);
				break;
#endif
			case 'l':
				log_fn = strdup(optarg);
				break;
			case 'S':
				sf_schema = strdup(optarg);
				break;
			case 'c':
				comment = strdup(optarg);
				break;
			case 'r':
				sscanf(optarg,"%d",&sf_loops);
				break;
			case 'p':
				sscanf(optarg,"%d",&sf_pause);
				break;
			case 'm':
				sscanf(optarg,"%d",&max_delta);
				break;
			case 'n':
				sscanf(optarg,"%d",&min_delta);
				break;
//added 2007032 by alex; todo: check if this works!
			case 'q': //set which source is used --> beam must be off then
				sscanf(optarg,"%d",&tmp);
				if ((1 <= tmp) || (tmp <= 5)) {
					modus &= 0xfffe;
					modus |= (0x0001 << (3+tmp));
				}
				break;
			case 'N': // h113 closed -> no beam!
				modus &= 0xfffe; //clear bit 0
				modus |= 0x0002; //set bit 1, this is somehow redundant
				break;
			case 'O': // shutter down closed
				modus |= 0x0008; //set bit 3
				break;
			case 'P': // shutter up closed
				modus |= 0x0004; //set bit 2
				break;
			case 'a': // scan is done
				modus |= 0x0200; //set bit 9
				break;
			case '?':
				cerr << "-- exiting because of unknown option --\n\n";
				exit( 1 );
				break;
			case 0:
				switch (long_opt_flag) {
					case 1:
						sscanf(optarg, "%x", &qdc0_addr);
						break;
					case 2:
						sscanf(optarg, "%x", &qdc1_addr);
						break;
					case 3:
						sscanf(optarg, "%x", &qdc2_addr);
						break;
					case 4:
						sscanf(optarg, "%x", &qdc3_addr);
						break;
					case 5:
						sscanf(optarg, "%d", &qdc_start_delay);
						break;
					case 6:
						sscanf(optarg, "%d", &qdc_stop_delay);
						break;
					case 7:
						sscanf(optarg, "%d", &qdc_gate_time);
						break;
					default:
						cerr << "WARNING: cannot process some long option (yet), ignoring" << endl;
				} 
				break;
			default:
				cerr << "-- exiting because of unknown option --\n\n";
				exit( 1 );
				break;
		}
	}
	sfs_length = strlen(sf_schema);
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void read_hw()
/*---------------------------------------------------------------------------*/
{
        // read TimeCounter
        int readnumber = tc->readFIFO32DMA(delta*2,tcbuf);
        if ( (readnumber % 2) != 0) {
                cerr << "\n!! number of read entries from TC is odd!! " << readnumber << endl;
        }
                
        if (lbox_conn) {
                // read out QDCs
                //bzero(qdcbuf, sizeof(qdcbuf));
		(*qdc0)[1].ReadEvents(qdcbuf[0][0], delta);
		(*qdc0)[2].ReadEvents(qdcbuf[0][1], delta);
		(*qdc0)[3].ReadEvents(qdcbuf[0][2], delta);
		(*qdc1)[1].ReadEvents(qdcbuf[1][0], delta);
		(*qdc1)[2].ReadEvents(qdcbuf[1][1], delta);
		(*qdc1)[3].ReadEvents(qdcbuf[1][2], delta);
		(*qdc2)[1].ReadEvents(qdcbuf[2][0], delta);
		(*qdc2)[2].ReadEvents(qdcbuf[2][1], delta);
		(*qdc2)[3].ReadEvents(qdcbuf[2][2], delta);
		(*qdc3)[1].ReadEvents(qdcbuf[3][0], delta);
		(*qdc3)[2].ReadEvents(qdcbuf[3][1], delta);
		(*qdc3)[3].ReadEvents(qdcbuf[3][2], delta);
	} else {
		// read out ADCs
		adc0->readFIFO32DMA(0,delta,adcbuf[0][0]);
		adc0->readFIFO32DMA(1,delta,adcbuf[0][1]);
		adc0->readFIFO32DMA(2,delta,adcbuf[0][2]);
		adc1->readFIFO32DMA(0,delta,adcbuf[1][0]);
		adc1->readFIFO32DMA(1,delta,adcbuf[1][1]);
		adc1->readFIFO32DMA(2,delta,adcbuf[1][2]);
		adc2->readFIFO32DMA(0,delta,adcbuf[2][0]);
		adc2->readFIFO32DMA(1,delta,adcbuf[2][1]);
		adc2->readFIFO32DMA(2,delta,adcbuf[2][2]);
		adc3->readFIFO32DMA(0,delta,adcbuf[3][0]);
		adc3->readFIFO32DMA(1,delta,adcbuf[3][1]);
		adc3->readFIFO32DMA(2,delta,adcbuf[3][2]);
	}
	
	// read Latch
	latch->readFIFO32DMA(delta,latchbuf);

	// read TDC
	for(u_int32_t i=0; i< delta; i++) {
		int32_t r = readTDCEvent(tdcbuf[i], TDC_STOPS, &(tdc_numstops[i]), &tdc_zeroevents);

		// the following sanity check does not work if there are no TDC events?!
#if 1
		if( (r==0) && (last_tdc_en==1023) )
			tdc_eco++;
		else if (((last_tdc_en+1) != r)) { // FIXME: ?!? > 0
			stringstream ss;

			ss << "read_hw(): error: last_tdc_en==" << last_tdc_en
					<< "  r==" << r << endl;

			throw IOError( ss.str().c_str(),r );
		}
		last_tdc_en = r;
#else
		last_tdc_en++;
#endif
	}
	
	// read TimeCounterChopper
	delta_chopper = cnt2->getC() - readcounter_chopper;
	tc_chopper->readFIFO32DMA(delta_chopper*2,tcbuf_chopper);
	
	u_int32_t tc_data_read_chopper, k=0, k_last=0;
	for(u_int32_t i=0; i<delta_chopper; i++) {
		k = (tcbuf_chopper_ring_end + i) % tcbuf_chopper_ringsize;	
				//points on tcbuf_chopper_ring
		k_last = (tcbuf_chopper_ring_end + i-1) % tcbuf_chopper_ringsize;	
				//points on last entry in tcbuf_chopper_ring
		tc_data_read_chopper = (((0x00ff & tcbuf_chopper[2*i+1])<<16) 
				| (0xffff & tcbuf_chopper[2*i]))+expander_tc_data_chopper * 16777216; 
				//value from tcbuf_chopper
		if (tc_data_read_chopper < tcbuf_chopper_ring[k_last]) expander_tc_data_chopper++;	
				//overflow of TimerCounterChopper
		tcbuf_chopper_ring[k] =(((0x00ff & tcbuf_chopper[2*i+1])<<16) 
				| (0xffff & tcbuf_chopper[2*i]))+expander_tc_data_chopper * 16777216; 
				//put (corrected) time to tcbuf_chopper_ring
	}
	tcbuf_chopper_ring_end = (tcbuf_chopper_ring_end+delta_chopper) % tcbuf_chopper_ringsize; 
			//points to "new" last entry in tcbuf_chopper_ring
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void write_event_data()
/*---------------------------------------------------------------------------*/
{
	u_int32_t tc_data=0, num=0;

	u_int32_t adc_last_was_empty[4][3] = 
			{ {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0} };
	
	// rearange data and write to file
	for (u_int32_t i = 0; i < (delta-lastread); i++) {

		//check for adc fifo full or fifo empty
		for (int j = 0; j < 4; j++) {
			for (int k = 0; k < 3; k++) {
				if (adcbuf[j][k][i] & 0x8000) {
					if (adc_last_was_empty[j][k]) {
						stringstream ss;
						
						ss <<"write_event_data():\n  "
								<<"FIFO EMPTY: adc["<<j<<"]["<<k<<"]["<<i<<"]\n"
								<<"              delta=" << delta << "\n              "
								<<"adc_last_was_empty[][] ==\n              "
								<<"{ {"
								<< adc_last_was_empty[0][0] << ","
								<< adc_last_was_empty[0][1] << ","
								<< adc_last_was_empty[0][2] << "}, {"
								<< adc_last_was_empty[1][0] << ","
								<< adc_last_was_empty[1][1] << ","
								<< adc_last_was_empty[1][2] << "}, {"
								<< adc_last_was_empty[2][0] << ","
								<< adc_last_was_empty[2][1] << ","
								<< adc_last_was_empty[2][2] << "}, {"
								<< adc_last_was_empty[3][0] << ","
								<< adc_last_was_empty[3][1] << ","
								<< adc_last_was_empty[3][2] << "} }\n\n";

						throw IOError(ss.str().c_str(),0);
						//cerr << ss.str();
					}

					adc_last_was_empty[j][k] = 1;
				} else 
					adc_last_was_empty[j][k] = 0;

				if(adcbuf[j][k][i] & 0x4000) {
					stringstream ss;

					ss << "write_event_data():\n  "
							<< "FIFO FULL: adc["<<j<<"]["<<k<<"]["<<i<<"]\n\n";
					throw IOError(ss.str().c_str(),0);
					cerr << ss.str();
				}
			}
		}
			
		// build tc data
		tcbuf_read = ((0x00ff & tcbuf[2*i+1])<<16) | (0xffff & tcbuf[2*i]);
		if ( measuretime > 16 ) {
			if (tcbuf_read < tcbuf_previous_read) expander_tc_data++;		
	//		tc_data_read=(((0x00ff & tcbuf[2*i+1])<<16) | (0xffff & tcbuf[2*i]))+expander_tc_data * 16777216;
	// 		if ((i==0 && (tc_data_read < TimerCounter)) || (i>0 && (tc_data_read < tc_data))) expander_tc_data++;
	// 		tc_data = (((0x00ff & tcbuf[2*i+1])<<16) | (0xffff & tcbuf[2*i]))+expander_tc_data * 16777216;
		}
		
		tc_data = tcbuf_read + expander_tc_data * 16777216;
		tcbuf_previous_read = tcbuf_read;
	
// 		tc_data = ((0x00ff & tcbuf[2*i+1])<<16) | (0xffff & tcbuf[2*i]);
	
		for (uint32_t jj=0; jj<tcbuf_chopper_ringsize; jj++) {
			num = (chopper_buf_scan + jj) % tcbuf_chopper_ringsize;
// 			cerr <<" i="<<i<<" num="<<num<<",ring_end="<<tcbuf_chopper_ring_end;
// 			cerr << " tc_data=" << tc_data << ",tc_data_chopper " << tcbuf_chopper_ring[num] << endl;
			if ((tc_data < tcbuf_chopper_ring[num]) || (num == tcbuf_chopper_ring_end)) {
				break;
			} else {
// 				if (last_tc_data_chopper<tcbuf_chopper_ring[num])
				last_tc_data_chopper = tcbuf_chopper_ring[num];
			}
		}
		chopper_buf_scan = (num+tcbuf_chopper_ringsize) % tcbuf_chopper_ringsize;
		TimerCounterChopper = last_tc_data_chopper;
		DeltaTimerCounterChopper = tc_data - TimerCounterChopper;

		if (tc_data < TimerCounterChopper) {
			consistency |= TC_INCONSISTENT;
			cerr << "tc_data < TimerCounterChopper" << endl;
			cerr << "i                        = " << i << endl;
			cerr << "hitcounter               = " << hitcounter << endl;
			cerr << "readcounter              = " << readcounter << endl;
			cerr << "DeltaTimerCounterChopper = " << DeltaTimerCounterChopper << endl;
			cerr << "expander_tc_data         = " << expander_tc_data << endl;
			cerr << "tc_data                  = " << tc_data << endl;
			cerr << "tcbuf_read               = " << tcbuf_read << endl;
			cerr << "TimerCounterChopper      = " << TimerCounterChopper << endl<<endl;
			break;
		}
		if (chopper_frequency > 0)
    		if (DeltaTimerCounterChopper > (u_int32_t) (1000000/chopper_frequency + 1000)) {
    			consistency |= TC_INCONSISTENT;
				cerr << "DeltaTimerCounterChopper > " << (u_int32_t) (1000000/chopper_frequency + 1000) << "us" << endl;
    			cerr << "i                        = " << i << endl;
    			cerr << "hitcounter               = " << hitcounter << endl;
    			cerr << "readcounter              = " << readcounter << endl;
    			cerr << "DeltaTimerCounterChopper = " << DeltaTimerCounterChopper << endl;
    			cerr << "expander_tc_data         = " << expander_tc_data << endl;
    			cerr << "tc_data                  = " << tc_data << endl;
    			cerr << "tcbuf_read               = " << tcbuf_read << endl;
    			cerr << "TimerCounterChopper      = " << TimerCounterChopper << endl<<endl;
    		}

		TimerCounter = tc_data;
		if (DeltaTimerCounter < TimerCounter)
			DeltaTimerCounter = TimerCounter - DeltaTimerCounter;
		else
			DeltaTimerCounter = TimerCounter;

		// status bits
		status_flags &= ~ (0x07 << 3);                 //set bits 5-3 to zero
		status_flags |= (tdc_numstops[i] & 0x07) << 3; //save number of tdc stops to bits 5-3

		// do latch stuff 
		u_int32_t latch_data;
		//the upper eight bits in latch are used as status flags!
		latch_data = 0x00ffffff & latchbuf[i];
#ifdef DKL
		// save status flags in upper 8 bits of latch data
		latch_data |= (((u_int32_t)status_flags) << 24);				
#endif
		Latch = latch_data;

/*
		cout << "write_event_data(): status_flags= 0x"
				<< hex << (((u_int32_t)status_flags)<<24)
				<< "  latch_data = 0x" << latch_data
				<< dec << endl;
*/

		// summarized TDC content for detector 1 and 2
		unsigned ev1 = GetTdcEvents(tdcbuf[i], TDC_STOPS, tdc_channel1, Det1Tdc, TDC_DET_STOPS);
		unsigned ev2 = GetTdcEvents(tdcbuf[i], TDC_STOPS, tdc_channel2, Det2Tdc, TDC_DET_STOPS);
		
		// check, which detector triggered first
		DetFirst = 0;
		if ( Det1Tdc[0] != 0 && Det2Tdc[0] != 0) {					//backscattered events
			if ( Det1Tdc[0] < Det2Tdc[0]) {
				DetFirst = 1;										//Det1 triggered first
			} else {
				DetFirst = 2;										//Det2 triggered first
			}
		} else if ( Det1Tdc[0] != 0 ) {
			DetFirst = 1;
		} else if ( Det2Tdc[0] != 0 ) {
			DetFirst = 2;
		}
			
		// check for missing detector TDC events 
		if ((ev1 + ev2) == 0) {
			cerr << endl << "WARNING: missing detector TDC events:" << endl << "tdc_channels = ";
			for (unsigned idx = 0; idx < tdc_numstops[i]; idx++) {
				unsigned n = (tdcbuf[i][idx] & 0x7f000000) >> 24;
				cerr << dec << n << " ";
			}
			cerr << "(" << tdc_numstops[i] << " events)" << flush << endl;
		}
		
		// retrieve per channel information 
		// NOTE: mapping of channels is hardcode here!
		for (int channel = 0; channel < 6; channel ++)
			GetTdcEvents(tdcbuf[i], TDC_STOPS, channel + 8, PmtTdc[0][channel], TDC_PMT_STOPS);
		for (int channel = 0; channel < 6; channel ++)
			GetTdcEvents(tdcbuf[i], TDC_STOPS, channel + 18, PmtTdc[1][channel], TDC_PMT_STOPS);

		// copy ADC data to root data structures
		if (lbox_conn) {
			Pmt[0][0] = qdcbuf[0][0][i];
			Pmt[0][1] = qdcbuf[0][1][i];
			Pmt[0][2] = qdcbuf[0][2][i];
			Pmt[0][3] = qdcbuf[1][0][i];
			Pmt[0][4] = qdcbuf[1][1][i];
			Pmt[0][5] = qdcbuf[1][2][i];
			Pmt[1][0] = qdcbuf[2][0][i];
			Pmt[1][1] = qdcbuf[2][1][i];
			Pmt[1][2] = qdcbuf[2][2][i];
			Pmt[1][3] = qdcbuf[3][0][i];
			Pmt[1][4] = qdcbuf[3][1][i];
			Pmt[1][5] = qdcbuf[3][2][i];
		} else {
			// order of ADC channels is from top to bottom 
			// TODO: should change this in ADC object, not here
			Pmt[0][0] = (adcbuf[0][2][i] & 0xffff);
			Pmt[0][1] = (adcbuf[0][1][i] & 0xffff);
			Pmt[0][2] = (adcbuf[0][0][i] & 0xffff);
			Pmt[0][3] = (adcbuf[1][2][i] & 0xffff);
			Pmt[0][4] = (adcbuf[1][1][i] & 0xffff);
			Pmt[0][5] = (adcbuf[1][0][i] & 0xffff);
			Pmt[1][0] = (adcbuf[2][2][i] & 0xffff);
			Pmt[1][1] = (adcbuf[2][1][i] & 0xffff);
			Pmt[1][2] = (adcbuf[2][0][i] & 0xffff);
			Pmt[1][3] = (adcbuf[3][2][i] & 0xffff);
			Pmt[1][4] = (adcbuf[3][1][i] & 0xffff);
			Pmt[1][5] = (adcbuf[3][0][i] & 0xffff);
		}
		DetSum[0] = DetSum[1] = 0;
		for (int kk=0; kk<6; kk++) {
			DetSum[0] += Pmt[0][kk];
			DetSum[1] += Pmt[1][kk];
		}

		// store current cycle number
		Cycle = cur_cycle + fcID;

		// for debugging the TC-problem
		EventNumber = readcounter + i;
		
		
		// write event data to root file
		tree->Fill();
		
#ifdef DKL
		EventStruct es = {
			0xffff & adcbuf[0][0][i], 
			0xffff & adcbuf[0][1][i],
			0xffff & adcbuf[0][2][i],
			0xffff & adcbuf[1][0][i],
			0xffff & adcbuf[1][1][i],
			0xffff & adcbuf[1][2][i],
			0xffff & adcbuf[2][0][i],
			0xffff & adcbuf[2][1][i],
			0xffff & adcbuf[2][2][i],
			0xffff & adcbuf[3][0][i],
			0xffff & adcbuf[3][1][i],
			0xffff & adcbuf[3][2][i],
			tdcbuf[i][0], tdcbuf[i][1], tdcbuf[i][2], tdcbuf[i][3],
			tc_data, 
			latch_data
		};

#if TIME_PROFILE
		TM_START();
#endif
		gzwrite(gzout,(const voidp)&es,sizeof(es));
#if TIME_PROFILE
		TM_STOP();
		gzWriteTime += tm_secs;
#endif
#endif // DKL

#ifdef ADC_ADJUSTMENT
		//for ADC GAIN- and OFFSET-calibration
		if (i == 0) 
			for (u_int32_t pmts = 0; pmts < 12; pmts++) 
				lowpeak[pmts] = highpeak[pmts] = 0;
		if (i % 2)
			for (u_int32_t pmts = 0; pmts < 12; pmts++)
				lowpeak[pmts] += Pmt[pmts/6][pmts-6*(pmts/6)];
		else
			for (u_int32_t pmts = 0; pmts < 12; pmts++)
				highpeak[pmts] += Pmt[pmts/6][pmts-6*(pmts/6)];

		if (!((i + 1) % (2 * averagenum ))) {
			system("clear");
			cerr << "\n\nDet1[0]\t\tDet1[3]\t\tDet2[0]\t\tDet2[3]\n" 
				<< lowpeak[0]/averagenum << " , " << highpeak[0]/averagenum << "\t"
				<< lowpeak[3]/averagenum << " , " << highpeak[3]/averagenum << "\t"
				<< lowpeak[6]/averagenum << " , " << highpeak[6]/averagenum << "\t"
				<< lowpeak[9]/averagenum << " , " << highpeak[9]/averagenum;
			cerr << "\n\n\nDet1[1]\t\tDet1[4]\t\tDet2[1]\t\tDet2[4]\n" 
				<< lowpeak[1]/averagenum << " , " << highpeak[1]/averagenum << "\t"
				<< lowpeak[4]/averagenum << " , " << highpeak[4]/averagenum << "\t"
				<< lowpeak[7]/averagenum << " , " << highpeak[7]/averagenum << "\t"
				<< lowpeak[10]/averagenum << " , " << highpeak[10]/averagenum;
			cerr << "\n\n\nDet1[2]\t\tDet1[5]\t\tDet2[2]\t\tDet2[5]\n" 
				<< lowpeak[2]/averagenum << " , " << highpeak[2]/averagenum << "\t"
				<< lowpeak[5]/averagenum << " , " << highpeak[5]/averagenum << "\t"
				<< lowpeak[8]/averagenum << " , " << highpeak[8]/averagenum << "\t"
				<< lowpeak[11]/averagenum << " , " << highpeak[11]/averagenum;
			
			for (u_int32_t pmts = 0; pmts < 12; pmts++)
				lowpeak[pmts] = highpeak[pmts] = 0;
		}
#endif
		// remember time of last event
		DeltaTimerCounter = TimerCounter;
	}

	//at this time tc_data holds the time of the last word read
	if (hitcounter - rate_hc > 1000) {
		rate = ((double)(hitcounter-rate_hc))/(tc_data-rate_tc) * 1000;
		rate_tc = tc_data;
		rate_hc = hitcounter;
	}

}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void write_run_data()
/*---------------------------------------------------------------------------*/
{
}


/*---------------------------------------------------------------------------*/
void write_cycle_data()
/*---------------------------------------------------------------------------*/
{
	dklheader->setCounter(0, cur_cycle, cnt1->getA());
	dklheader->setCounter(1, cur_cycle, cnt1->getB());
	dklheader->setCounter(2, cur_cycle, cnt1->getC());
	dklheader->setCounter(3, cur_cycle, cnt2->getA());
	dklheader->setCounter(4, cur_cycle, cnt2->getB());
	dklheader->setCounter(5, cur_cycle, cnt2->getC());
	dklheader->setCounter(6, cur_cycle, startstop->getCounter1());
	dklheader->setCounter(7, cur_cycle, startstop->getCounter2());
	dklheader->setCounter(8, cur_cycle, startstop->getCounter3());
	dklheader->setCounter(9, cur_cycle, startstop->getTimerCounter());
	//this marks bad cycles if any exist
	dklheader->setCounter(CYCLE_VALID_COUNTER, cur_cycle, consistency);

	// update ROOT branches
	Cycle = cur_cycle + fcID;
	for (unsigned idx = 0; idx < noc; idx++)
		Counter[idx] = dklheader->getCounter(idx, cur_cycle);
	Valid = (consistency == 0);
	SelectorSpeed = unsigned(
						(double)dklheader->getCounter(4, cur_cycle) *
						STARTSTOP_CLOCK / 
						(double)dklheader->getCounter(9, cur_cycle)
					);
	ChopperSpeed = unsigned(
			(double)dklheader->getCounter(5, cur_cycle) *
			STARTSTOP_CLOCK / 
			(double)dklheader->getCounter(9, cur_cycle)
					);
	// write ROOT cycle data
	CycleTree->Fill();
}


/*---------------------------------------------------------------------------*/
void rootfile_header_pre()
{
/*
	TObjString header_title = "dies ist der inhalt";
	char * key = "root_file_header";
	header_title.Write(key);
*/
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void rootfile_header_post()
{
	stringstream ev_count_stream, modus_stream, sfs_length_stream, sf_loops_stream,
			fcID_stream, cycle_count_stream, fileOK_stream, noc_stream, selector_stream;

	ev_count_stream     << ev_count; 
	modus_stream        << modus;
	sfs_length_stream   << sfs_length;
	sf_loops_stream     << sf_loops;
	fcID_stream         << fcID;
	cycle_count_stream  << sfs_length * sf_loops;
	fileOK_stream       << (dklheader->fileOK == 0);
	noc_stream          << noc;
	selector_stream     << SelectorSpeed;
	
	TObjString ev_count_header     = ev_count_stream.str().c_str();
	TObjString modus_header        = modus_stream.str().c_str();
	TObjString sfs_length_header   = sfs_length_stream.str().c_str();
	TObjString sf_loops_header     = sf_loops_stream.str().c_str();
	TObjString fcID_header         = fcID_stream.str().c_str();
	TObjString cycle_count_header  = cycle_count_stream.str().c_str();
	TObjString fileOK_header       = fileOK_stream.str().c_str();
	TObjString noc_header          = noc_stream.str().c_str();
	TObjString SelectorSpeed_header= selector_stream.str().c_str();

	ev_count_header.Write("Events");
	modus_header.Write("Modus");
	sfs_length_header.Write("sfs_length");
	sf_loops_header.Write("sf_loops");
	fcID_header.Write("FirstCycle");
	cycle_count_header.Write("NumberOfCycles");
	fileOK_header.Write("Valid");
	noc_header.Write("NumberOfCounters");
	SelectorSpeed_header.Write("SelectorSpeed");
}
/*---------------------------------------------------------------------------*/


// returns the event number, extracted from header
/*---------------------------------------------------------------------------*/
u_int32_t readTDCEvent(u_int32_t *buf, u_int32_t bufsize, u_int32_t *tdc_numstops, u_int32_t *tdc_zeroevents)
		throw (IOError)
/*---------------------------------------------------------------------------*/
{
	//FIXME: This function requires knowledge about internals of the TDC.
	//       It should therefore be moved to the TDC class, or a new class "TDCEvent" or similar.
	u_int32_t d;  // last data from tdc

	if (!bufsize)
		throw IOError("readTDCEvent():\n"
					"  invalid bufsize (=0)", bufsize);

	// clear buffer
	for (unsigned i = 0; i < bufsize; i++) 
		buf[i] = 0;

	// seek and read header word
	unsigned retries = 0; // number of words skipped
	do { 
		d = tdc_br->readWord(); retries++; 
		// d = tdc->readWord(); retries++;
	} while ((retries < tdc_br_cs) && ((d & TDC_V767::TYPE_MASK) != TDC_V767::HEADER));

	if (retries >= tdc_br_cs)
		throw IOError("readTDCEvent():\n  header expected",d);

	//extract event number from header
	u_int32_t r = d & TDC_V767::EN_MASK;

	*tdc_numstops = 0; 
	u_int32_t eob_numstops;
	do {
		d=tdc_br->readWord();
//		d=tdc->readWord();

		if ((d & TDC_V767::TYPE_MASK) == TDC_V767::IV_DATA)
			throw IOError("readTDCEvent():\n  invalid data",d);	

		if ((d & TDC_V767::TYPE_MASK) == TDC_V767::EOB) {
			if ((d & TDC_V767::EOB_STATUS_MASK) == TDC_V767::EOB_STATUS_ERROR )
				throw IOError("readTDCEvent(): TDC chip error",d);

			eob_numstops = (d & TDC_V767::EOB_EVENT_DATA_COUNTER);
			if( eob_numstops != *tdc_numstops )
				throw IOError( "readTDCEvent(): number of stops read"
						" is different from that given in EOB",d );
			break;
		}

		if ((d & TDC_V767::TYPE_MASK) == TDC_V767::VALID_DATA ) {
			if( *tdc_numstops >= bufsize ) {
		//		throw IOError("readTDCEvent():\n"
		//              	  "  to many time values for buffer",d);

				cerr << "readTDCEvent():"
					<< "  warning: too many time values for buffer: "
					<< "tdc_numstops=" << *tdc_numstops << " bufsize=" << bufsize << "\n"
					<< "  continue ignoring data...\n";
			} else {
					buf[*tdc_numstops] = d;
			}
			(*tdc_numstops)++;
		}
	} while(1);

	if (*tdc_numstops > bufsize) {
		cerr << "readTDCEvent(): there are " << *tdc_numstops
			<< " tdc stop events saving as " << bufsize << endl;
		*tdc_numstops = bufsize;
	}

	if (*tdc_numstops == 0) {
//		cerr << "readTDCEvent(): warning: tdc_numstops==0, eob_numstops="
//	 		 << eob_numstops << endl;

		(*tdc_zeroevents)++;
	}

	return r;
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
int GetTdcEvents(u_int32_t *tdc, u_int32_t bufsize, unsigned channel, UInt_t *dest, unsigned max_stops)
/*---------------------------------------------------------------------------*/
{
	unsigned count = 0;

	// go through list of events TDC to find events for this channel
	for (unsigned i = 0; i < bufsize; i++) {
		u_int32_t ch = ((tdc[i] & 0x7f000000) >> 24);
		if (ch == channel) {
			dest[count] = tdc[i] & 0xfffff;
			count++;
			if (count >= max_stops)
				break;
		}
	}

	// fill rest of list with zeros
	for (unsigned j = count; j < max_stops; j++)
		dest[j] = 0;

	// return number of TDC events for this channel
	return count;
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void signal_handler(int signal)
/*---------------------------------------------------------------------------*/
{
	if(signal == SIGINT) {
		cerr << "\n\nCTRL-C pressed\n";
		safe_exit(2);
	} else
		psignal(signal, "signal_handler(): unknown signal!");
}
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
void safe_exit(int r)
/*---------------------------------------------------------------------------*/
{
	cerr << "\nsafe_exit()\n";

	if (crate > -1 ) {
		setFlipper('0');
		cerr << "vmesysreset...";
		if (int r = vmesysreset(crate)) {
			cerr << "error: vmesysreset() returned " << r << endl << endl;
			cerr.flush(); 
			exit(1);
		}
		sleep(1);	
		cerr << "ok\n";

		cerr << "closing crate\n\n"; close(crate);
	}

	cerr.flush(); 
	exit(r);
}
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
void read_cycleno()
/*---------------------------------------------------------------------------*/
{
	std::ifstream in(CYCLE_FN);

	if(!in) {
		throw IOError((string("read_cycleno():\n"
				"  error by opening file \"") + CYCLE_FN +"\"\n\n").c_str(),0);
	}

	in >> fcID;

	in.close();
}
/*---------------------------------------------------------------------------*/

//write next cycles' id number to file
//open file that contains next fcID for writing
/*---------------------------------------------------------------------------*/
void write_nextcycleno()
/*---------------------------------------------------------------------------*/
{
	ofstream next_fcID_file(CYCLE_FN);

	if(!next_fcID_file) {
		throw IOError((string("write_nextcycleno():\n"
				"---- ERROR: could not open file \"") + CYCLE_FN
				+ "\" that contains next fcID for writing ----\n\n"
				).c_str(),0);
	}

	next_fcID_file << dec << (cur_cycle + fcID);
	next_fcID_file.close();
}
/*---------------------------------------------------------------------------*/
